/**
 ******************************************************************************
 * @file           : bsp_ds18b20.c
 * @brief          : DS18B20 Temperature Sensor Driver (STM32F4xx)
 ******************************************************************************
 */

#include "./Devices/DS18B20/bsp_ds18b20.h"
#include "./Devices/DS18B20/bsp_ds18b20_config.h"

#define DS18B20_SKIP_ROM        0xCC
#define DS18B20_CONVERT_T       0x44
#define DS18B20_READ_SCRATCHPAD 0xBE

static void ds18b20_delay_us(uint16_t us)
{
    uint32_t cycles = us * 168 / 4;
    while (cycles--);
}

static void ds18b20_write_bit(uint8_t bit)
{
    if (bit)
    {
        HAL_GPIO_WritePin(DS18B20_GPIO_PORT, DS18B20_GPIO_PIN, GPIO_PIN_RESET);
        ds18b20_delay_us(10);
        HAL_GPIO_WritePin(DS18B20_GPIO_PORT, DS18B20_GPIO_PIN, GPIO_PIN_SET);
        ds18b20_delay_us(55);
    }
    else
    {
        HAL_GPIO_WritePin(DS18B20_GPIO_PORT, DS18B20_GPIO_PIN, GPIO_PIN_RESET);
        ds18b20_delay_us(65);
        HAL_GPIO_WritePin(DS18B20_GPIO_PORT, DS18B20_GPIO_PIN, GPIO_PIN_SET);
        ds18b20_delay_us(5);
    }
}

static uint8_t ds18b20_read_bit(void)
{
    uint8_t bit = 0;
    
    HAL_GPIO_WritePin(DS18B20_GPIO_PORT, DS18B20_GPIO_PIN, GPIO_PIN_RESET);
    ds18b20_delay_us(10);
    HAL_GPIO_WritePin(DS18B20_GPIO_PORT, DS18B20_GPIO_PIN, GPIO_PIN_SET);
    ds18b20_delay_us(10);
    
    if (HAL_GPIO_ReadPin(DS18B20_GPIO_PORT, DS18B20_GPIO_PIN))
    {
        bit = 1;
    }
    
    ds18b20_delay_us(45);
    return bit;
}

static void ds18b20_write_byte(uint8_t byte)
{
    for (int i = 0; i < 8; i++)
    {
        ds18b20_write_bit(byte & (1 << i));
    }
}

static uint8_t ds18b20_read_byte(void)
{
    uint8_t byte = 0;
    
    for (int i = 0; i < 8; i++)
    {
        byte |= (ds18b20_read_bit() << i);
    }
    
    return byte;
}

static uint8_t ds18b20_reset(void)
{
    uint8_t presence = 0;
    
    HAL_GPIO_WritePin(DS18B20_GPIO_PORT, DS18B20_GPIO_PIN, GPIO_PIN_RESET);
    ds18b20_delay_us(480);
    HAL_GPIO_WritePin(DS18B20_GPIO_PORT, DS18B20_GPIO_PIN, GPIO_PIN_SET);
    ds18b20_delay_us(70);
    
    if (!HAL_GPIO_ReadPin(DS18B20_GPIO_PORT, DS18B20_GPIO_PIN))
    {
        presence = 1;
    }
    
    ds18b20_delay_us(410);
    return presence;
}

uint8_t ds18b20_init(void)
{
    GPIO_InitTypeDef gpio_init = {0};
    
    __HAL_RCC_GPIOA_CLK_ENABLE();
    
    gpio_init.Pin = DS18B20_GPIO_PIN;
    gpio_init.Mode = GPIO_MODE_OUTPUT_OD;
    gpio_init.Pull = GPIO_PULLUP;
    gpio_init.Speed = GPIO_SPEED_FREQ_HIGH;
    HAL_GPIO_Init(DS18B20_GPIO_PORT, &gpio_init);
    
    HAL_GPIO_WritePin(DS18B20_GPIO_PORT, DS18B20_GPIO_PIN, GPIO_PIN_SET);
    
    if (ds18b20_reset())
    {
        log_i("DS18B20 found");
        return 0;
    }
    
    log_e("DS18B20 not found");
    return 1;
}

void ds18b20_deinit(void)
{
    HAL_GPIO_DeInit(DS18B20_GPIO_PORT, DS18B20_GPIO_PIN);
}

uint8_t ds18b20_start_convert(void)
{
    if (!ds18b20_reset())
    {
        return 1;
    }
    
    ds18b20_write_byte(DS18B20_SKIP_ROM);
    ds18b20_write_byte(DS18B20_CONVERT_T);
    
    return 0;
}

uint8_t ds18b20_read_temp(float *temp)
{
    uint8_t scratchpad[9];
    int16_t raw_temp;
    
    if (!ds18b20_reset())
    {
        return 1;
    }
    
    ds18b20_write_byte(DS18B20_SKIP_ROM);
    ds18b20_write_byte(DS18B20_READ_SCRATCHPAD);
    
    for (int i = 0; i < 9; i++)
    {
        scratchpad[i] = ds18b20_read_byte();
    }
    
    raw_temp = (int16_t)((scratchpad[1] << 8) | scratchpad[0]);
    *temp = raw_temp / 16.0f;
    
    return 0;
}
